use crate::global;
use core_api::checksum;
use core_api::db;
use std::collections::HashSet;
use std::ffi::OsString;
use std::path::Path;
use sysinfo::{Pid, ProcessesToUpdate, System};
use tauri::{command, AppHandle, Runtime, Window};
use tauri_plugin_shell::process::CommandEvent;
use tauri_plugin_shell::ShellExt;
type Result<T> = std::result::Result<T, String>;

pub const FFMPEG: &str = "ff7scre.exe";
const OUTPUT_F: u8 = 'f' as u8;

#[command]
pub async fn start_recording<R: Runtime>(
    app: AppHandle<R>,
    window: Window<R>,
    output_file: &Path,
    watermark_text: String,
    force_open_camera: bool, // has_callback: bool,
                             // record_status_channel: Channel<String>,
) -> Result<u32> {
    let ff_pid = global::read().get_ffmpeg_pid();
    if ff_pid > 0 {
        return Ok(ff_pid);
    }
    // 确保上级目录存在
    if let Some(dest_path) = output_file.parent() {
        let _ = std::fs::create_dir_all(dest_path);
    }

    // set_default_save_path(&app, &window);
    // 保存到全局状态
    global::write().set_output_file(output_file.to_path_buf());
    let outfile = output_file.to_str().unwrap(); //global::read().get_save_path_name();
                                                 // 优化参数 https://ask.csdn.net/questions/8072127
    let is_open_camera;
    let cmd = match detect_camara_mic(&app, &window).await {
        // 有摄像头和麦克风
        (Some(camara), Some(mic)) => {
            is_open_camera = true;
            // ffmpeg -y -probesize 10M -f gdigrab -r 10 -t 28800 -i desktop -f dshow -i video="RGB Camera" -f dshow -i audio="外部麦克风 (Realtek(R) Audio)" -filter_complex "[0]scale=1024:-1[desktop]; [1]scale=160:120[cam]; [desktop][cam]overlay=5:5,drawtext=text='%{localtime}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=text='%{localtime}':x=mod(100*t\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4[video]; [2]anull[audio]"  -map "[video]" -map "[audio]" -b:v 550k -c:v libx264 -c:a aac all.mp4
            app.shell().sidecar(FFMPEG).unwrap()
            .args(&[
                //"-loglevel", "info" ,"-err_detect" ,"ignore_err" , "-report",
                "-hide_banner",
                "-y",
                "-probesize","10M",
                "-f",
                "gdigrab",
                "-r",
                "10",
                 "-t",
                 "28800",
                "-i",
                "desktop",
                 "-f","dshow","-i",format!("video={}", camara).as_str(),
                 "-f","dshow","-i",format!("audio={}", mic).as_str(),
                 "-filter_complex",
                 format!("[0]scale=1024:-1[desktop];[1]scale=160:-1 [cam]; [desktop][cam]overlay=5:5,drawtext=fontfile=msyh.ttf:text='{} %{{localtime}}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=fontfile=msyh.ttf:text='%{{localtime}}':x=mod(100*t\\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4[video]; [2]anull[audio]", watermark_text).as_str(),
                 //format!("[0]scale=1024:-1[desktop];[1]scale=160:-1 [cam]; [desktop][cam]overlay=5:5,drawtext=fontfile=msyh.ttf:text='{} %{{localtime}}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=fontfile=msyh.ttf:text='%{{localtime}}'drawtext=text='%{{localtime}}':x=mod(t*50\\,w):y=h-30:box=0:fontcolor=gray@0.4[video]; [2]anull[audio]", watermark_text).as_str(),
                 "-map", "[video]", "-map" ,"[audio]", 
                 "-b:v", "550k", 
                 "-c:v", "libx264" ,"-c:a", "aac", 
                 "-fflags" ,"+flush_packets",
                 "-flush_packets" ,"1", "-f", "matroska" ,
                 outfile,
                 // 输出摄像头截图,不需要截图可以注掉一下2行
                 //"-map","1","-f","image2","-vf", format!("fps={}", 1).as_str(),
                 //"-update","1","tempc"
             ])
        }
        // 有摄像头和 无麦克风
        (Some(camara), None) => {
            is_open_camera = true;
            //ffmpeg -y -probesize 100M -f gdigrab -r 10 -i desktop -f dshow -i video="FULL HD 1080P Webcam" -filter_complex "[0]scale=1024:-1[desktop];[1]scale=160:120[cam];[desktop][cam]overlay=5:5,drawtext=text='%{localtime}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=text='%{localtime}':x=mod(100*t\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4" -b:v 550k -c:v libx264 all.mp4
            app.shell().sidecar(FFMPEG).unwrap()
            .args(&[
                //"-loglevel", "info" ,"-err_detect" ,"ignore_err" , "-report",
                "-hide_banner",
                "-y",
                "-probesize","10M",
                "-f",
                "gdigrab",
                "-r",
                "10",
                 "-t",
                 "28800",
                "-i",
                "desktop",
                 "-f","dshow","-i",format!("video={}", camara).as_str(),
                 "-filter_complex", 
                 format!("[0]scale=1024:-1[desktop];[1]scale=160:-1 [cam]; [desktop][cam]overlay=5:5,drawtext=fontfile=msyh.ttf:text='{} %{{localtime}}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=fontfile=msyh.ttf:text='%{{localtime}}':x=mod(100*t\\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4", watermark_text).as_str(),
                 "-b:v", "550k", 
                 "-c:v", "libx264" ,"-c:a", "aac", 
                "-fflags" ,"+flush_packets",
                 "-flush_packets" ,"1", "-f", "matroska" ,
                 outfile,
                 // 输出摄像头截图,不需要截图可以注掉一下2行
                 //"-map","1","-f","image2","-vf", format!("fps={}", 1).as_str(),
                 //"-update","1","tempc"
             ])
        }
        // 无摄像头和 有麦克风
        (None, Some(mic)) => {
            is_open_camera = false;
            //ffmpeg -y -probesize 100M -f gdigrab -r 10 -i desktop -f dshow -i audio="外部麦克风 (Realtek(R) Audio)" -filter_complex "[0]scale=1024:-1[desktop];[desktop]drawtext=text='%{localtime}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=text='%{localtime}':x=mod(100*t\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4[video];[1]anull[audio]" -map "[video]" -map "[audio]" -b:v 550k -c:v libx264 -c:a aac all.mp4
            app.shell().sidecar(FFMPEG).unwrap()
            .args(&[
               // "-loglevel", "info" ,"-err_detect" ,"ignore_err" , "-report",
                "-hide_banner",
                "-y",
                "-probesize","10M",
                "-f",
                "gdigrab",
                "-r",
                "10",
                 "-t",
                 "28800",

                // "-fs","4096M",

                "-i",
                "desktop",
                 "-f","dshow","-i",format!("audio={}", mic).as_str(),
                 "-filter_complex",
                 format!("[0]scale=1024:-1[desktop];[desktop]drawtext=fontfile=msyh.ttf:text='{} %{{localtime}}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=fontfile=msyh.ttf:text='%{{localtime}}':x=mod(100*t\\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4[video]; [1]anull[audio]", watermark_text).as_str(),
                // format!("[0]scale=1024:-1[desktop];[desktop]drawtext=fontfile=msyh.ttf:text='{} %{{localtime}}':x=5:y=5:fontsize=12:fontcolor=red,drawtext=fontfile=msyh.ttf:text='%{{localtime}}':x=mod(t*50\\,w):y=h-30:box=0:fontcolor=gray@0.4[video]; [1]anull[audio]", watermark_text).as_str(),
                 "-map", "[video]", "-map" ,"[audio]", 
                 "-b:v", "550k", 
                 "-c:v", "libx264" ,"-c:a", "aac", 
                "-fflags" ,"+flush_packets",
                 "-flush_packets" ,"1", "-f", "matroska" ,
                 outfile,
             ])
        }
        // 无摄像头和无麦克风
        _ => {
            is_open_camera = false;
            app.shell().sidecar(FFMPEG).unwrap()
            .args(&[
               // "-loglevel", "info" ,"-err_detect" ,"ignore_err" , "-report",
                "-hide_banner",
                "-y",
                "-f",
                "gdigrab",
                "-i",
                "desktop",
                "-r",
                "10",
                 "-t",
                 "28800",
                 // "-fs","4096M",
                 "-b:v",
                 "550k",
                "-vf",
                format!("scale=1024:-1,drawtext=fontfile=msyh.ttf:text='{} %{{localtime}}':fontsize=12:fontcolor=red:x=1:y=1,drawtext=fontfile=msyh.ttf:text='%{{localtime}}':x=mod(100*t\\,w):y=abs(sin(t))*h*0.9:box=0:fontcolor=gray@0.4", watermark_text).as_str(),
                //format!("scale=1024:-1,drawtext=fontfile=msyh.ttf:text='{} %{{localtime}}':fontsize=12:fontcolor=red:x=1:y=1,drawtext=fontfile=msyh.ttf:text='%{{localtime}}':x=mod(t*50\\,w):y=h-30:box=0:fontcolor=gray@0.4", watermark_text).as_str(),
               "-fflags" ,"+flush_packets",
                "-flush_packets" ,"1", "-f", "matroska" ,
                outfile,
            ])
        }
    };
    if force_open_camera && !is_open_camera {
        return Err("无法开启摄像头!".to_string());
    }

    if let Ok((mut rx, child)) = cmd.spawn() {
        let mut g = global::write();
        let pid = child.pid();
        g.set_ffmpeg(Some(child));

        //if has_callback {
        //接收输出信息
        tauri::async_runtime::spawn(async move {
            // read events such as stdout
            let mut n = 1;
            let mut is_first = true;
            while let Some(event) = rx.recv().await {
                // ffmpeg 不在stdou中输出在Stderr 输出
                if let CommandEvent::Stderr(line_bytes) = event {
                    // 只判断第一个byte 是否等于'f'
                    if true || OUTPUT_F == line_bytes[0] {
                        //println!("{},{},{}==>{}", first_byte,'f' as u8,first_byte=='f' as u8,&line);
                        let line = String::from_utf8_lossy(&line_bytes);
                        println!("FFMPEG:{}", &line);
                        // 输出的信息格式
                        //frame=    0 fps=0.0 q=0.0  size=       0KiB time=N/A bitrate=N/A dup=0 drop=116 speed=N/A
                        //frame=    5 fps=0.7 q=26.0 size=       0KiB time=00:00:00.30 bitrate=   1.3kbits/s dup=0 drop=125 speed=0.0448x
                        if n % 2 == 0 {
                            if line.starts_with("frame=") {
                                let p1 = line.find("time=");
                                let p2 = line.find("bitrate=");
                                if let (Some(start), Some(end)) = (p1, p2) {
                                    let time_str = &line[start + "time=".len()..end - 4];
                                    if !time_str.starts_with("N/A") {
                                        if is_first {
                                            //let _ = app.emit(EVENT_START_RECORDING, "START");
                                            global::read().get_message_manager().send_msg(
                                                db::Message::new(
                                                    db::MsgFlag::RECORDING,
                                                    db::MsgStatus::START,
                                                    format!("{}", pid),
                                                    String::default(),
                                                ),
                                            );
                                            is_first = false;
                                        }
                                        // https://tauri.app/develop/calling-frontend/#global-events
                                        // let _ = app.emit(EVENT_START_RECORDING, time_str);

                                        global::read().get_message_manager().send_msg(
                                            db::Message::new(
                                                db::MsgFlag::RECORDING,
                                                db::MsgStatus::PROGRESS,
                                                format!("{}", pid),
                                                time_str.to_string(),
                                            ),
                                        );

                                        // 要考虑页面跳转和刷新的可能性，channel 目前不支持重新监听
                                        //let _ = record_status_channel.send(time_str.to_string());
                                        //tokio::time::sleep(tokio::time::Duration::from_secs(1)).await; // 暂停1秒
                                    }
                                }
                            }
                        }
                        n += 1;
                    }
                } else if let CommandEvent::Terminated(payload) = event {
                    // 这里表示进程已经失败
                    //let _ = app.emit(EVENT_START_RECORDING, "STOP");
                    //db::send_msg(EVENT_RECRODING,"STOP","");
                    global::read()
                        .get_message_manager()
                        .send_msg(db::Message::new(
                            db::MsgFlag::RECORDING,
                            db::MsgStatus::SUCCESS,
                            format!("{}", pid),
                            String::default(),
                        ));
                    //let _ = app.emit(EVENT_STOP_RECORDING, global::read().get_output_file());
                    //let _ = record_status_channel.send("STOP".to_string());
                    //===terminated:TerminatedPayload { code: Some(-22), signal: None }
                    global::write().set_ffmpeg(None);

                    log::info!("===recording terminated:{:?}", payload);
                }
            }
        });
        //}
        Ok(pid)
    } else {
        // tauri::api::dialog::message(Some(&window), "提示", "无法启动录屏进程!");
        // app.dialog()
        //     .message("无法启动录屏进程!")
        //     .kind(MessageDialogKind::Error)
        //     .blocking_show();
        Err("无法启动录屏进程!".to_string())
    }
}

/// 探测摄像头和麦克风
async fn detect_camara_mic<R: Runtime>(
    app: &AppHandle<R>,
    _window: &Window<R>,
) -> (Option<String>, Option<String>) {
    let mut camara: Option<String> = None;
    let mut mic: Option<String> = None;

    // 探测 摄像头和 麦克风
    let cmd = app.shell().sidecar(FFMPEG).unwrap().args(&[
        "-list_devices",
        "true",
        "-f",
        "dshow",
        "-i",
        "dummy",
    ]);

    let cmd_run = cmd.output().await;
    //println!("================{:?}", &cmd_run);
    if let Ok(output) = cmd_run {
        let err_bytes = output.stderr;
        let err = String::from_utf8_lossy(&err_bytes);

        //const START_STR: &str = r#"] ""#; //ffmpeg7.0 中间有1个空格
        const START_STR: &str = r#"]  ""#; //ffmpeg4.4 中间有2个空格
        if output.status.success()
            || (output.status.code().is_some() && output.status.code().unwrap() > 1)
        {
            //ffmpeg4.4 返回结果为1
            // [dshow @ 000001f3b006f9c0] "Integrated Camera" (video)
            // [dshow @ 000001f3b006f9c0] "外部麦克风 (Realtek(R) Audio)" (audio)
            let device_list = err
                .lines()
                .filter(|line| {
                    let ok = line.starts_with("[dshow")
                        && ((line.ends_with("(video)") || line.ends_with("(audio)")) // ffmpeg7.0
                            || (line.to_lowercase().contains("cam") || line.contains("麦克"))); // ffmpeg4.4
                                                                                                // if ok {
                                                                                                //     println!("{},ok={}", line, ok);
                                                                                                // }
                    ok
                })
                .map(|line| {
                    if let Some(start) = line.find(START_STR) {
                        return Some(&line[start + START_STR.len()..]);
                    }
                    None
                    //Some(line)
                })
                .filter(|line| line.is_some())
                .map(|line| line.unwrap())
                .inspect(|line| println!("list deivces==>{}", line)) //输出调试
                .collect::<Vec<&str>>();
            // 输出
            //Integrated Camera" (video)
            //外部麦克风 (Realtek(R) Audio)" (audio)
            //麦克风阵列 (Realtek(R) Audio)" (audio)

            // 获取可用的麦克

            for device in &device_list {
                if device.ends_with("(audio)") || device.contains("麦克") {
                    let end = device.find(r#"""#).unwrap();
                    let device_name = &device[..end];

                    if let Ok(output) = app
                        .shell()
                        .sidecar(FFMPEG)
                        .unwrap()
                        .args(&[
                            "-y",
                            "-loglevel",
                            "quiet",
                            "-f",
                            "dshow",
                            "-i",
                            format!("audio={}", device_name).as_str(),
                            // r#"audio=外部麦克风 (Realtek(R) Audio)"#, //这里不用加双引号
                            "-aframes",
                            "1",
                            "testmic.aac",
                        ])
                        .output()
                        .await
                    {
                        // println!("{}  {:?},{}",device_name,output.status,output.stderr);
                        println!("AUDIO:[{}],  {:?}", device_name, output.status);
                        if output.status.success() {
                            //return Some(device_name);
                            mic = Some(device_name.to_string());
                            break;
                        }
                    }
                }
            }

            // 获取可用的摄像头
            // ffmpeg -f dshow -i video="FULL HD 1080P Webcam" -vframes 1 screenshot.png

            for device in &device_list {
                if (device.ends_with("(video)") || device.to_lowercase().contains("cam"))
                    && !device.contains("screen")
                {
                    let end = device.find(r#"""#).unwrap();
                    let device_name = &device[..end];

                    if let Ok(output) = app
                        .shell()
                        .sidecar(FFMPEG)
                        .unwrap()
                        .args(&[
                            "-y",
                            "-loglevel",
                            "quiet",
                            "-f",
                            "dshow",
                            "-i",
                            format!("video={}", device_name).as_str(),
                            "-vframes",
                            "1",
                            "testcamara.png",
                        ])
                        .output()
                        .await
                    {
                        println!("VIDEO:[{}],  {:?}", device_name, output.status);
                        if output.status.success() {
                            camara = Some(device_name.to_string());
                            break;
                        }
                    }
                }
            }
        }
        println!("camara: {:?} , mic:{:?}", &camara, &mic);
    }
    (camara, mic)
}

#[command]
pub fn stop_recording() -> Result<Option<String>> {
    let pid = global::write().stop_ffmpeg();
    if pid > 0 {
        // 重试次数
        let mut retry_times = 0;
        while retry_times <= 10 && global::write().stop_ffmpeg() > 0 {
            std::thread::sleep(std::time::Duration::from_secs(1));
            println!("stop_recording  retry_times: {:?} ", &retry_times);
            retry_times = retry_times + 1;
        }

        while retry_times <= 20 && !global::write().kill_ffmpeg() {
            std::thread::sleep(std::time::Duration::from_secs(1));
            println!("kill_recording  retry_times: {:?} ", &retry_times - 10);
            retry_times = retry_times + 1;
        }

        //如果 进程仍然存在，尝试杀死它
        let pid = global::read().get_ffmpeg_pid();

        if pid > 0 {
            let success = kill_process(pid);
            println!("===================kill  recording is none pid={}", pid);
            if !success {
                let r = kill_process_by_names(vec![FFMPEG.to_string()]);
                println!("===================kill by name  recording {:?}", r);
            }
        }

        if global::write().stop_ffmpeg() == 0 {
            //此处已经保证进程已经关闭
            if let Some(outfile) = global::read().get_output_file() {
                // todo:这里可以调用ffmpeg 写入元信息
                return Ok(checksum::generate_checksum(outfile.to_str().unwrap()));
            }
        }
    }
    Ok(None)
}

pub fn kill_process(pid: u32) -> bool {
    let mut system = System::new_all();
    system.refresh_processes(ProcessesToUpdate::Some(&[Pid::from_u32(pid)]), true); // 更新进程信息

    if let Some(process) = system.process(Pid::from_u32(pid)) {
        println!("find: {} - {:?}", pid, process.name());
        process.kill(); // 默认等价于 SIGTERM（Windows 是 TerminateProcess）
        true
    } else {
        println!("can not find PID: {}", pid);
        false
    }
}

pub fn kill_process_by_names(target_names: Vec<String>) -> Vec<(OsString, u8)> {
    let mut result: Vec<(OsString, u8)> = vec![]; // 存储返回结果
    let name_set: HashSet<OsString> = target_names
        .iter()
        .map(|s| OsString::from(s.to_ascii_lowercase())) // 转小写进行匹配
        .collect();

    let mut system = System::new(); // 不加载全部信息
    system.refresh_processes(ProcessesToUpdate::All, true);
    for (pid, process) in system.processes() {
        let name = process.name().to_ascii_lowercase(); // 获取进程名并转为小写
                                                        //println!("进程===========: {:?} ", process.name());
        if name_set.contains(&name) {
            // 尝试终止进程
            let killed = if process.kill() {
                println!("success kill  {:?} [PID: {}]", process.name(), pid);
                1 // 成功杀死进程，返回 1
            } else {
                println!("can not kill  {:?} [PID: {}]", process.name(), pid);
                0 // 无法杀死进程，返回 0
            };
            result.push((process.name().to_os_string(), killed));
        }
    }

    result
}

/// 获取当前所有进程的名称（可能包含重复）
pub fn get_all_process() -> Vec<String> {
    let mut system = System::new();
    system.refresh_processes(ProcessesToUpdate::All, true); // 仅刷新进程列表

    system
        .processes()
        .values()
        .map(|proc| proc.name().to_string_lossy().to_string())
        .collect()
}

pub fn check_process(target_names: Vec<String>) -> Vec<(String, u8)> {
    let mut system = System::new();
    system.refresh_processes(ProcessesToUpdate::All, true);

    // 收集当前正在运行的进程名（统一小写）
    let running_processes: HashSet<OsString> = system
        .processes()
        .values()
        .map(|p| p.name().to_ascii_lowercase())
        .collect();

    // 遍历目标进程名，判断是否存在
    target_names
        .iter()
        .map(|name| {
            let lower = OsString::from(name.to_ascii_lowercase());
            let exists = if running_processes.contains(&lower) {
                1
            } else {
                0
            };
            (name.clone(), exists)
        })
        .collect()
}

#[command]
pub async fn validate_checksum<R: Runtime>(
    _app: AppHandle<R>,
    output_file: &Path,
) -> Result<String> {
    checksum::validate_checksum(output_file.to_str().unwrap())
}
